﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NewsMine.Utilities;
using System.Threading;
using NewsDiscoveryEngine;
using NewsMine.Storage;
using NewsDiscoveryEngine.Cleansing;
using NewsMine.DomainObjects;
using NewsDiscoveryEngine.Jobs;
using NewsMine.Storage.SQLite;
using System.Web.Caching;
using NewsDiscoveryEngine.Index;

namespace WinApp.JobEngine.Hosting
{
    public partial class Form1 : Form
    {

        private BackgroundWorker bgWorkerForIndexing;
        private BackgroundWorker bgWorkerForProcessingFeed;
        private static bool isFeedRetrievalProcessInProgress = false;
        private static DateTime lastRetrievedTime = DateTime.MinValue;
        private static DateTime lastIndexedTime = DateTime.MinValue;

        private static bool isIndexingInProgress = false;

        DateTime applicationStartTime = DateTime.UtcNow;
        bool isRestartRequested = false;

        private List<Thread> runningThreads = new List<Thread>();

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.Text = "Crawler - " + NewsMine.Configuration.ConfigurationManager.StorageBaseFolder;

            int numberOfThreads = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings.Get("NumberOfDownloadingThreads"));

            bool isThreadsEnabled = Convert.ToBoolean(System.Configuration.ConfigurationManager.AppSettings.Get("isThreadsEnabled"));

            if (isThreadsEnabled == true)
            {
                for (int i = 0; i < numberOfThreads; i++)
                {
                    Thread th = new Thread(ProcessDownLoadingJobs);
                    th.Name = "Downloader" + i.ToString();
                    th.Priority = ThreadPriority.BelowNormal;
                    th.Start();

                    runningThreads.Add(th);
                }

                Thread threadProcessFeeds = new Thread(ProcessFeedRetrievalFromWebPages);
                threadProcessFeeds.Name = "FeedProcessor";
                threadProcessFeeds.Priority = ThreadPriority.AboveNormal;
                threadProcessFeeds.Start();

                runningThreads.Add(threadProcessFeeds);

                Thread threadIndexing = new Thread(ProcessIndexing);
                threadIndexing.Priority = ThreadPriority.Normal;
                threadIndexing.Name = "Indexer";
                threadIndexing.Start();

                runningThreads.Add(threadIndexing);

                Thread threadImageSizeCalculator = new Thread(ProcessImageSizeCalculationFromQueue);
                threadImageSizeCalculator.Priority = ThreadPriority.Normal;
                threadImageSizeCalculator.Name = "Img Size calc";
                threadImageSizeCalculator.Start();

                runningThreads.Add(threadImageSizeCalculator);
            }
            else
            {
                for (int i = 0; i < numberOfThreads; i++)
                {
                    BackgroundWorker bgDownloader = new BackgroundWorker();
                    bgDownloader.DoWork += new DoWorkEventHandler(bgDownloader_DoWork);
                    bgDownloader.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgDownloader_RunWorkerCompleted);

                    bgDownloader.RunWorkerAsync();
                }


                btnSchceduleCrawlingJobs_Click(null, null);
                btnStartIndexing_Click(null, null);
            }
        }

        private void btnSchceduleCrawlingJobs_Click(object sender, EventArgs e)
        {
            //this.tmrForFeedRetrieval_Tick(null, null);
            this.bgWorkerForProcessingFeed = new BackgroundWorker();
            this.bgWorkerForProcessingFeed.DoWork += new DoWorkEventHandler(this.bgWorkerForProcessingFeed_DoWork);
            this.bgWorkerForProcessingFeed.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorkerForProcessingFeed_RunWorkerCompleted);

            this.bgWorkerForProcessingFeed.RunWorkerAsync();
            btnSchceduleCrawlingJobs.Enabled = false;
        }

        private void btnStartIndexing_Click(object sender, EventArgs e)
        {
            this.bgWorkerForIndexing = new BackgroundWorker();
            this.bgWorkerForIndexing.DoWork += new DoWorkEventHandler(this.bgWorkerForIndexing_DoWork);
            this.bgWorkerForIndexing.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorkerForIndexing_RunWorkerCompleted);
            this.bgWorkerForIndexing.RunWorkerAsync();
            btnStartIndexing.Enabled = false;
        }

        void bgWorkerForIndexing_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                TempLogger.Log("Indexing background worker thread got error." + Environment.NewLine + e.Error.Message);
                TempLogger.Log("Error is " + Environment.NewLine + e.Error.StackTrace);
                MessageBox.Show("Indexing thread is aborted with some error, Its better to stop and strat the application");
                MessageBox.Show("Error message is : " + e.Error.Message + "   - " + e.Error.StackTrace);

                btnStartIndexing_Click(null, null);
            }
            else
                TempLogger.Log("Indexing background worker thread got finished its job");
        }

        private void bgWorkerForIndexing_DoWork(object sender, DoWorkEventArgs e)
        {
            ProcessIndexing();
        }

        private void bgWorkerForProcessingFeed_DoWork(object sender, DoWorkEventArgs e)
        {
            ProcessFeedRetrievalFromWebPages();
        }

        private static void ProcessFeedRetrievalFromWebPages()
        {
            while (!NewsDiscoveryEngine.JobEngine.IsStopJobRequested)
            {

                if (!isFeedRetrievalProcessInProgress && (DateTime.UtcNow - lastRetrievedTime) > TimeSpan.FromMinutes(20))
                {
                    isFeedRetrievalProcessInProgress = true;
                    lastRetrievedTime = DateTime.UtcNow;
                    

                    try
                    {
                        if (Defaults.DownloadingJobQueue.GetCount() == 0 && Defaults.JobQueue.GetCount() == 0)
                        {
                            TempLogger.Log("Job scheduling started...");
                            new ProcessWebUrl2FeedWorkFlow().Initialize();
                            TempLogger.Log("Job scheduling over in wf.Initialize method.");
                        }
                        else
                        {
                            TempLogger.Log("Job scheduling skipped.");
                        }

                        TempLogger.Log("Process jobs by job engine... started");
                        JobsManager jobsManager = new JobsManager();

                        jobsManager.ProcessJobsByJobEngine();

                        TempLogger.Log("Process jobs by job engine... end...");
                    }
                    catch (Exception ex)
                    {
                        TempLogger.Log("Error while processing Feed retrieval from web pages", ex);
                    }
                    finally
                    {

                        isFeedRetrievalProcessInProgress = false;
                    }
                }
                else
                {
                    if (!NewsDiscoveryEngine.JobEngine.IsStopJobRequested)
                        Thread.Sleep(5 * 1000);
                }

            }
        }



        private void ProcessIndexing()
        {
            while (!NewsDiscoveryEngine.JobEngine.IsStopJobRequested)
            {
                if (!isIndexingInProgress && (DateTime.UtcNow - lastIndexedTime) > TimeSpan.FromMinutes(1))
                {
                    isIndexingInProgress = true;
                    lastIndexedTime = DateTime.UtcNow;

                    try
                    {
                        TempLogger.Log("Indexing started...");




                        IndexHtmlFeedItemJob.IndexFeedFromIndexQueue();

                        //todo: this needs to be fixed; with some flag... Right now it is taking long time due to sqlite query

                        //IndexHtmlFeedItemJob.IndexFeedThatIsUpdatedInSqliteStore();
                        TempLogger.Log("Indexing over.....");
                    }
                    catch (Exception ex)
                    {
                        TempLogger.Log("Error while Indexing...", ex);
                    }
                    finally
                    {
                        isIndexingInProgress = false;
                    }
                }
                else
                {
                    if (!NewsDiscoveryEngine.JobEngine.IsStopJobRequested)
                    {
                        Thread.Sleep(20 * 1000);
                    }
                }
            }

        }


        #region Processing Refreshing WebSite Content And In CacheStore 

        private void ProcessingRefreshingWebSiteContentAndInCacheStore()
        {
            string cookie = "refreshbot";
            //Response.Headers.Add("isfresh", "n");

            while (!NewsDiscoveryEngine.JobEngine.IsStopJobRequested)
            {
                try
                {
                    //get latest feed id number 
                    // compare with previously saved feed id 
                    // now decide we have enough delay and also we have enough feeds to ahead with process.
                    
                    // and if there are enough feeds and enough delay, then check what are all the domains affected.... generate the html for that not every domain.

                    // find affected affected language and atag values generate for them.... its basically channel.... we can consider getting all the channels 
                    // and run the facet count and find the changes and generate the html..

                    // by category, by language, by category

                    var indxer = FeedIndexSearcher.Instance;
                    int totalRecs = 0;
                    string query = "";
                    int pastHours = 0;
                    string contextQuery1 = FacetReader.GetTimeLimitQuery(DateTime.UtcNow.AddHours(-1 * pastHours), DateTime.UtcNow, "pubdate");

                    string finalQueryForFeedsRetrival = "(" + query + ") AND (" + contextQuery1 + ")";

                    var feeds = indxer.GetFeedItemsByQueryOrderByLatest(finalQueryForFeedsRetrival, 1, 1000, out totalRecs, true);

                    SQLiteKeyValueHelper _SQLiteKeyValueHelper = ((NewsMine.Storage.SQLite.SQLiteKeyValueStore)(Defaults.HtmlCacheStore)).GetSqliteHelperClass();


                    //this method needs to be called, for making sure data is committed.
                    _SQLiteKeyValueHelper.MakeSurePendingSetOperationsAndRemoveOperationsArePersisted(true);

                }
                catch (Exception ex)
                {
                    TempLogger.Log("Error while Indexing...", ex);
                }
                finally
                {
                    isIndexingInProgress = false;
                }
            }

        }


        #endregion

        private void ProcessImageSizeCalculationFromQueue()
        {
            while (!NewsDiscoveryEngine.JobEngine.IsStopJobRequested)
            {
                try
                {
                    if (Defaults.ImageSizeCalculationQueue.GetCount() > 0)
                    {
                        TempLogger.Log("Image size calculation started ...");
                        object objFeedItem = Defaults.ImageSizeCalculationQueue.Dequeue();

                        if (objFeedItem == null)
                            continue;

                        FeedItem feedItem = (FeedItem)objFeedItem;

                        CalculateFeedImageSizeJob.CalculateImageSizeForFeedImage(feedItem);

                        if (feedItem.ImageWidth > 0 && feedItem.ImageHeight > 0)
                        {
                            Defaults.IndexingQueue.Enqueue(feedItem);
                        }
                    }
                    else
                    {
                        if (!NewsDiscoveryEngine.JobEngine.IsStopJobRequested)
                        {
                            Thread.Sleep(3 * 1000);
                        }
                    }

                }
                catch (Exception ex)
                {
                    TempLogger.Log("Error while Missing Image size calculating in .....", ex);

                    continue;
                }
                finally
                {

                }
            }

        }

        DateTime lastTimeFacebookRetryQueueProcessed = DateTime.MinValue;
        object syncLockForFBQueue = new object();
        DateTime LastTimeFacebookRetryQueueProcessed
        {
            get
            {
                lock (syncLockForFBQueue)
                {
                    return lastTimeFacebookRetryQueueProcessed;
                }

            }
            set
            {
                lock (syncLockForFBQueue)
                {
                    lastTimeFacebookRetryQueueProcessed = value;
                }
            }
        }
        bool isFacebookProcessInProgress = false;

        private void ProcessFacebookPagePostRequests()
        {

            if (!NewsDiscoveryEngine.JobEngine.IsStopJobRequested)
            {

                if ((DateTime.UtcNow - LastTimeFacebookRetryQueueProcessed) > TimeSpan.FromSeconds(120))
                {

                    try
                    {
                        while (Defaults.FaceBookRetryJobQueue.GetCount() > 0)
                        {
                            Defaults.FaceBookDownloadingJobQueue.Enqueue(Defaults.FaceBookRetryJobQueue.Dequeue());
                        }
                    }
                    catch (Exception ex)
                    {
                        TempLogger.Log("Error while moving jobs from Facebook Retry Queue to Download queue", ex);
                    }



                    LastTimeFacebookRetryQueueProcessed = DateTime.UtcNow;
                }
                try
                {

                    PrepareFacebookPostDataFromUserSelectionJob job =
                        (PrepareFacebookPostDataFromUserSelectionJob)Defaults.FaceBookDownloadingJobQueue.Dequeue();

                    if (job != null)
                    {
                        TempLogger.Log("JOb facebook download started...");

                        job.ExecuteJob();

                        TempLogger.Log("JOb facebook download over.....");
                    }
                    else
                        return;

                }
                catch (Exception ex)
                {
                    TempLogger.Log("Error while executing facebook download job......", ex);
                }
                finally
                {
                }

            }

            //FeedItem feedItem = (FeedItem) Defaults.HtmlFeedStore.Get(url,true);

            //if (feedItem == null)
            //    throw new ApplicationException("Corresponding feed is not found for the url");
        }


        void bgWorkerForProcessingFeed_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                TempLogger.Log("Feed Processing background worker thread got error." + Environment.NewLine + e.Error.Message);
                TempLogger.Log("Error is " + Environment.NewLine + e.Error.StackTrace);
                MessageBox.Show("Job processing thread is aborted with some error, Its better to stop and strat the application");
                MessageBox.Show("Error message is : " + e.Error.Message + "   - " + e.Error.StackTrace);
                btnSchceduleCrawlingJobs_Click(null, null);
            }
            else
                TempLogger.Log("Feed processing background worker thread got finished its job");
        }


        bool isClosed = true;

        private void btnOpenIdentify_Click(object sender, EventArgs e)
        {
            if (isClosed)
            {
                isClosed = false;

                Menu_Identifier form = new Menu_Identifier();
                var result = form.ShowDialog();

                isClosed = true;
            }
        }

        private void btnPerformCleansing_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Yet to be implemented");
        }


        void bgDownloader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                TempLogger.Log("Downloading bg workder got errored : ", e.Error);
            }
            else
                TempLogger.Log("Downloading background worker thread got finished its job");
        }

        void bgDownloader_DoWork(object sender, DoWorkEventArgs e)
        {
            ProcessDownLoadingJobs();
        }



        object syncLock = new object();


        public void ProcessDownLoadingJobs()
        {
            JobsManager mgr = new JobsManager();

            bool isDownLoadQueueEmpty = false;

            while (!NewsDiscoveryEngine.JobEngine.IsStopJobRequested)
            {
                lock (syncLock)
                {
                    if (Defaults.DownloadingJobQueue.GetCount() == 0)
                    {
                        isDownLoadQueueEmpty = true;
                    }
                    else
                        isDownLoadQueueEmpty = false;
                }

                if (isDownLoadQueueEmpty == false)
                {
                    mgr.ProcessDownloadingJobsByJobEngine();
                }
                else
                {
                    if (!NewsDiscoveryEngine.JobEngine.IsStopJobRequested)
                    {

                        Thread.Sleep(2000);
                    }
                }
            }
        }

        private void btnStopAll_Click(object sender, EventArgs e)
        {
            NewsDiscoveryEngine.JobEngine.IsStopJobRequested = true;

            //isRestartRequested = true;
        }

        private void tmrMonitoringMaster_Tick(object sender, EventArgs e)
        {


            if (NewsDiscoveryEngine.JobEngine.IsStopJobRequested == true)
            {
                bool isAllThreadsStopped = true;
                foreach (var t in runningThreads)
                {
                    if (t.ThreadState == ThreadState.Stopped || t.ThreadState == ThreadState.Aborted)
                        continue;
                    else
                    {
                        isAllThreadsStopped = false;
                        break;
                    }
                }

                if (isFacebookProcessInProgress == true)
                {
                    isAllThreadsStopped = false;
                }

                if (isAllThreadsStopped == true && isRestartRequested)
                {
                    tmrMonitoringMaster.Enabled = false;
                    lblStatus.Text = "Applicaiton is restarting...... ";
                    Thread.Sleep(5000);
                    TempLogger.Log("Restart is triggered...");
                    //Application.Restart();

                    System.Diagnostics.ProcessStartInfo Info = new System.Diagnostics.ProcessStartInfo();
                    Info.Arguments = "/C ping 127.0.0.1 -n 2 && \"" + Application.ExecutablePath + "\"";
                    Info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
                    Info.CreateNoWindow = true;
                    Info.FileName = "cmd.exe";
                    System.Diagnostics.Process.Start(Info);
                    Application.Exit();

                }

            }
            else
            {
                if ((DateTime.UtcNow - applicationStartTime) > TimeSpan.FromMinutes(20))
                {
                    NewsDiscoveryEngine.JobEngine.IsStopJobRequested = true;
                    isRestartRequested = true;
                }
            }

            lblStatus.Text = "status:";

            lblStatus.Text = lblStatus.Text + Environment.NewLine + "Stop signal status :" + NewsDiscoveryEngine.JobEngine.IsStopJobRequested.ToString();

            foreach (var t in runningThreads)
            {
                lblStatus.Text = lblStatus.Text + Environment.NewLine + t.Name + " : " + t.ThreadState.ToString();
            }

            //if ((DateTime.UtcNow - LastTimeFacebookDownloadQueueProcessed) > TimeSpan.FromSeconds(5))

            if (!isFacebookProcessInProgress)
            {
                isFacebookProcessInProgress = true;
                try
                {
                    ProcessFacebookPagePostRequests();
                }
                catch (Exception ex)
                {
                    TempLogger.Log("Error while processing facebook stuff", ex);
                }
                finally
                {

                }

                isFacebookProcessInProgress = false;
            }
        }



    }
}
